// Copyright 2023 Huawei Cloud Computing Technology Co., Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "RemoteVideoEncoder.h"
#include <string.h>
#include <unistd.h>
#include <fstream>
#include "VideoEngine.h"
#include "HWSecure/include/securec.h"

namespace {
    constexpr int PRIMARY_COLOURS = 3;
    constexpr int COMPRESS_RATIO = 2;
    constexpr int FRAME_RATE_10 = 10;
    constexpr int FRAMERATE = 30;
    constexpr int FRAME_RATE_60 = 60;
    constexpr int BASELINE = 66;
    constexpr int MAIN = 77;
    constexpr int HIGH = 100;
    constexpr int MIN_CPU_NUM = 0;
    constexpr int MAX_CPU_NUM = 6;
    constexpr int FRAME_BASED = 0;
    constexpr int SLICE_BASED = 1;
    constexpr int LEVEL_H264 = 32;
    constexpr int LEVEL_H265 = 31;
    constexpr int MAIN_H265 = 1;
    constexpr int GOP_MIN = 30;
    constexpr int GOP_MAX = 5000;
    constexpr int MIN_BITRATE = 100000;
    constexpr int MAX_BITRATE = 40000000;
    constexpr int ONE_SECOND_IN_MS = 1000;            // 单位：ms

    const char* KEY_RC_MODE = "rcMode";
}

/*
 * @功能描述：获取当前时间精确到ms
*/
static long GetCurrentTimeMs()
{
    constexpr int oneSecond = 1000;
    timespec now;
    if(clock_gettime(CLOCK_REALTIME, &now) != 0) {
        ERR("get time failed");
    }
    return reinterpret_cast<long>(now.tv_sec * oneSecond + now.tv_nsec/(oneSecond*oneSecond));
}

RemoteVideoEncoder* RemoteVideoEncoder::g_instance = nullptr;

RemoteVideoEncoder* RemoteVideoEncoder::GetInstance()
{
    if (g_instance == nullptr) {
        g_instance = new(std::nothrow) RemoteVideoEncoder();
        if (g_instance == nullptr) {
            ERR("Encoder new failed");
        }
    }
    return g_instance;
}

int RemoteVideoEncoder::DestroyInstance()
{
    if (g_instance != nullptr) {
        if (g_instance->DestroyEncoder() < 0){
            ERR("Destory encoder failed");
            return -1;
        }
        delete g_instance;
        g_instance = nullptr;
        return 0;
    }
    ERR("Encoder engine has been destroyed.");
    return -1;
}

void RemoteVideoEncoder::ResetEncoder()
{
    if (!m_resetFlag) {
        return;
    }
    if(DestroyEncoder() != 0) {
        ERR("Destroy Encoder failed.");
    }
    VideoEncoderParam videoEncodeParam;
    videoEncodeParam.frameRate = m_frameRate;
    videoEncodeParam.bitrate = m_bitrate;
    videoEncodeParam.gopSize = m_gopSize;
    videoEncodeParam.profile = m_profile;
    videoEncodeParam.width = m_width;
    videoEncodeParam.height = m_height;
    videoEncodeParam.frameType = m_frameType;
    videoEncodeParam.encoderType = m_encoderType;
    videoEncodeParam.cpuCnt = m_cpuCnt;
    videoEncodeParam.sliceBased = m_sliceBased;

    if(InitEncoder(videoEncodeParam) != 0) {
        ERR("init Encoder failed.");
    }
    if(StartEncoder() != 0) {
        ERR("start Encoder failed.");
    }
    m_resetFlag = false;
}

/*
 * @功能描述：设置状态
 * @参数 [in] newStatus：引擎状态
 */
void RemoteVideoEncoder::SetStatus(const EngineStat newStatus)
{
    m_status = newStatus;
    INFO("Set status [%d]", static_cast<int>(newStatus));
}

/*
 * @功能描述：获取抓图引擎状态
 * @返回值：EngineStat引擎状态
 */
EngineStat RemoteVideoEncoder::GetStatus() const
{
    return m_status;
}

void RemoteVideoEncoder::SetStatics(const EncoderStatistics statics)
{
    m_encoderStatics = statics;
}


EncoderStatistics RemoteVideoEncoder::GetStatics() const
{
    return m_encoderStatics;
}

int RemoteVideoEncoder::SetParam(VideoEncoderParam &param)
{
    INFO("RemoteVideoEncoder set Param framerate is: %d, bitrate is: %d, gopsize is: %d, profile is: %d, ",
        param.frameRate, param.bitrate, param.gopSize, param.profile);
    if((param.frameRate < FRAME_RATE_10 || param.frameRate > FRAME_RATE_60 || (param.frameRate % 10 != 0))
        || param.bitrate < MIN_BITRATE
        || param.bitrate > MAX_BITRATE
        || param.gopSize < GOP_MIN
        || param.gopSize > GOP_MAX
        || param.cpuCnt < MIN_CPU_NUM
        || param.cpuCnt > MAX_CPU_NUM
        || param.sliceBased < FRAME_BASED
        || param.sliceBased > SLICE_BASED
        || (param.profile != BASELINE && param.profile != MAIN && param.profile != HIGH)) {
        m_resetFlag = false;
        return -1;
    }
    if (m_frameRate != param.frameRate
        || m_gopSize != param.gopSize
        || m_profile != param.profile
        || m_cpuCnt != param.cpuCnt
        || m_sliceBased != param.sliceBased) {
        m_frameRate = param.frameRate;
        m_bitrate = param.bitrate;
        m_gopSize = param.gopSize;
        m_profile = param.profile;
        m_cpuCnt = param.cpuCnt;
        m_sliceBased = param.sliceBased;
        m_resetFlag = true;
        return 0;
    }
    if (m_bitrate != param.bitrate) {
        m_bitrate = param.bitrate;
        INFO("bitrate changed, no restart RemoteEncoder...");
        this->avCtx->bit_rate = param.bitrate;
        if (this->m_rcMode == 0) { // abr
            this->avCtx->bit_rate = param.bitrate;  // 平均码率
            this->avCtx->rc_max_rate = param.bitrate;  // 最大瞬时码率，与rc_buffer_size配合使用
            this->avCtx->rc_buffer_size = param.bitrate;
        } else if (this->m_rcMode == 2) { // cbr
            this->avCtx->bit_rate = param.bitrate;  // 平均码率
            this->avCtx->rc_max_rate = param.bitrate;  // 最大瞬时码率，与rc_buffer_size配合使用
            this->avCtx->rc_buffer_size = param.bitrate / 5;
        } else if (this->m_rcMode == 3) { // capped crf
            this->avCtx->rc_max_rate = param.bitrate;
            this->avCtx->rc_buffer_size = param.bitrate / 3;

            if (m_rcBufferSize > 0) {
                this->avCtx->rc_buffer_size = m_rcBufferSize;
            }
        } else {
            INFO("CRF not suppot");
        }
    }
    return 0;
}
/*lint -save -e1401 */
RemoteVideoEncoder::RemoteVideoEncoder()
{
    this->frame = NULL;
}
/*lint -restore */

RemoteVideoEncoder::~RemoteVideoEncoder()
{
    INFO("RemoteVideoEncoder destructor start..");
    if (NULL != this->frame) {
        av_frame_free(&this->frame);
        this->frame = NULL;
    }
    if (NULL != this->avCtx) {
        avcodec_free_context(&this->avCtx);
        this->avCtx = NULL;
    }
    if (NULL != this->encPkt) {
        av_packet_free(&this->encPkt);
        this->encPkt = NULL;
    }
    INFO("RemoteVideoEncoder destructor finished");
}

int RemoteVideoEncoder::DestroyEncoder()
{
    mIsReadyForRepeat = false;
    INFO("Destroy RemoteEncoder start");
    uint32_t err = 0;
    if (NULL != this->frame) {
        av_frame_free(&this->frame);
        this->frame = NULL;
    }
    if (NULL != this->avCtx) {
        avcodec_free_context(&this->avCtx);
        this->avCtx = NULL;
    }
    if (NULL != this->encPkt) {
        av_packet_free(&this->encPkt);
        this->encPkt = NULL;
    }
    INFO("Destroy RemoteEncoder finish");
    m_status = VMI_ENCODE_ENGINE_INVALID;
    return err;
}

void RemoteVideoEncoder::SetOpt(std::string key, std::string value)
{
    if (key == KEY_RC_MODE) {
        int rcMode = std::atoi(value.c_str());
        if (this->m_rcMode != rcMode) {
            INFO("Current rcMode is %d, set rcMode to %d",m_rcMode, rcMode);
            m_rcMode = rcMode;
            if (!m_firstModeFlag) {
                INFO("not first time to set,ready to restart");
                m_resetFlag = true;
            }
        } else {
            INFO("rcMode not changed");
        }
    }
}

void RemoteVideoEncoder::InitRCParamsCPU(VideoEncoderParam &param)
{
    if (this->m_rcMode > 3) {
        WARN("invalid rc_mode");
        return;
    }
    if (this->m_rcMode == 0) {  // abr
        this->avCtx->bit_rate = param.bitrate;
        this->avCtx->rc_max_rate = param.bitrate;  // 最大瞬时码率，与rc_buffer_size配合使用
        this->avCtx->rc_buffer_size = param.bitrate;
        return;
    }
    if (this->m_rcMode == 2) {  // cbr
        this->avCtx->bit_rate = param.bitrate;
        this->avCtx->rc_max_rate = param.bitrate;  // 最大瞬时码率，与rc_buffer_size配合使用
        this->avCtx->rc_buffer_size = param.bitrate / 5;
        return;
    }
    // crf or capped crf
    if (av_opt_set(avCtx->priv_data, "crf", "23", 0) != 0) {
        ERR("av opt set crf failed");
    }
    if (param.frameType == FRAME_TYPE_H264 && av_opt_set(avCtx->priv_data, "crf_max", "51", 0) != 0) {
        ERR("av opt set crf_max failed");
    }
    if (this->m_rcMode == 3) {  // capped crf
        this->avCtx->rc_max_rate = param.bitrate;
        this->avCtx->rc_buffer_size = (m_rcBufferSize > 0) ? m_rcBufferSize : (param.bitrate / 3);
    }
}

void RemoteVideoEncoder::InitRCParamsGPU(VideoEncoderParam &param)
{
    if (this->m_rcMode > 3) {
        WARN("invalid rc_mode");
        return;
    }
    if (this->m_rcMode == 0) {  // abr
        this->avCtx->bit_rate = param.bitrate;
        this->avCtx->rc_max_rate = param.bitrate;  // 最大瞬时码率，与rc_buffer_size配合使用
        this->avCtx->rc_buffer_size = param.bitrate;
        return;
    }
    if (this->m_rcMode == 2) {  // cbr
        this->avCtx->bit_rate = param.bitrate;
        this->avCtx->rc_max_rate = param.bitrate;  // 最大瞬时码率，与rc_buffer_size配合使用
        this->avCtx->rc_buffer_size = param.bitrate / 5;
        return;
    }
    // crf or capped crf
    if (av_opt_set(avCtx->priv_data, "cq", "23", 0) != 0) {
        ERR("av opt set crf failed");
    }
    if (this->m_rcMode == 3) { // capped crf
        this->avCtx->rc_max_rate = param.bitrate;
        this->avCtx->rc_buffer_size = (m_rcBufferSize > 0) ? m_rcBufferSize : (param.bitrate / 3);
    }
}

void RemoteVideoEncoder::InitParamsCPU(VideoEncoderParam &param) 
{
    if (param.frameType == FRAME_TYPE_H264) {
        this->avCtx->level = LEVEL_H264;
        this->avCtx->profile = param.profile;  // 66: baseline, 77: main, 100: high
    } else {
        this->avCtx->level = LEVEL_H265;
        this->avCtx->profile = MAIN_H265;
    }
    INFO("InitParam m_rcMode is:%d", this->m_rcMode);
    this->InitRCParamsCPU(param);
    std::string tune = "zerolatency";
    if (av_opt_set(this->avCtx->priv_data, "preset", "veryfast", 0) != 0) {
        ERR("av opt set failed");
    }
    if (av_opt_set(this->avCtx->priv_data, "tune", tune.c_str(), 0) != 0) {
        ERR("av opt set failed");
    }
    if (av_opt_set(this->avCtx->priv_data, "intra-refresh", "1", 0) != 0) {
        ERR("av opt set failed");
    }
    INFO("gopsize is:%d, profile is:%d, tune is:%s", param.gopSize, this->avCtx->profile, tune.c_str());
}

void RemoteVideoEncoder::InitParamsGPU(VideoEncoderParam &param) 
{
    if (param.frameType == FRAME_TYPE_H264) {
        this->avCtx->level = LEVEL_H264;
        this->avCtx->profile = param.profile;  // 66: baseline, 77: main, 100: high
    } else {
        this->avCtx->level = LEVEL_H265;
        this->avCtx->profile = MAIN_H265;
    }
    INFO("InitParam m_rcMode is:%d",this->m_rcMode);
    this->InitRCParamsGPU(param);
    std::string tune = "ull";
    if (av_opt_set(this->avCtx->priv_data, "preset", "fast", 0) != 0) {
        ERR("av opt set preset failed");
    }
    if (av_opt_set(this->avCtx->priv_data, "tune", tune.c_str(), 0) != 0) {
        ERR("av opt set tune failed");
    }
    if (av_opt_set(this->avCtx->priv_data, "forced-idr", "1", 0) != 0) {
        ERR("av opt set forced-idr failed");
    }
    if (av_opt_set(this->avCtx->priv_data, "delay", "0", 0) != 0) {
        ERR("av opt set delay 0 failed");
    }
    INFO("gopsize is:%d, profile is:%d, tune is:%s", param.gopSize, this->avCtx->profile, tune.c_str());
}

void RemoteVideoEncoder::InitParams(VideoEncoderParam &param) 
{
    this->avCtx->width = param.width;
    this->avCtx->height = param.height;
    this->avCtx->time_base = (AVRational){1, static_cast<int>(param.frameRate)};
    this->avCtx->framerate = (AVRational){static_cast<int>(param.frameRate), 1};
    this->avCtx->gop_size = param.gopSize;  // 关键帧间隔
    this->avCtx->max_b_frames = 0;  // 去掉B帧
    this->avCtx->thread_count = param.cpuCnt;
    if (param.sliceBased) {
        this->avCtx->thread_type = FF_THREAD_SLICE;
    } else {
        this->avCtx->thread_type = FF_THREAD_FRAME;
    }
    this->avCtx->pix_fmt = AV_PIX_FMT_YUV420P;
    if (this->m_encoderType == ENCODER_TYPE_REMOTE_HOST_CPU) {
        this->InitParamsCPU(param);
    } else {
        this->InitParamsGPU(param);
    }
}

bool RemoteVideoEncoder::SetEncoderName()
{
    if (m_encoderType == ENCODER_TYPE_REMOTE_HOST_GPU) {
        if (m_frameType == FRAME_TYPE_H264) {
            this->encoderName = "h264_nvenc";
        } else {
            this->encoderName = "hevc_nvenc";
        }
    } else if (m_encoderType == ENCODER_TYPE_REMOTE_HOST_CPU) {
        if (m_frameType == FRAME_TYPE_H264) {
            this->encoderName = "libx264";
        } else {
            this->encoderName = "libx265";
        }
    } else {
        ERR("Encoder type is wrong, select either REMOTE_HOST_CPU or REMOTE_HOST_GPU");
        return false;
    }
    INFO("Selected encoder is %s", this->encoderName);
    return true;
}

int RemoteVideoEncoder::InitEncoder(VideoEncoderParam &param) 
{
    m_firstModeFlag = false;
    int err = -1;
    if (m_status != VMI_ENCODE_ENGINE_INVALID) {
        ERR("m_status is wrong...");
        return -1;
    }
    if ((param.width <= 0) || (param.height <= 0)) {
        ERR("Input width and height invalid.");
        return err;
    }

    this->m_lastTime = 0;
    this->m_totalCountPerSecond = 0;
    this->m_repeatFrameCount = 0;
    this->m_frameCount = 0;

    m_width = param.width;
    m_height = param.height;
    m_bitrate = param.bitrate;
    m_frameRate = param.frameRate;
    m_gopSize = param.gopSize;
    m_profile = param.profile;
    m_encoderType = param.encoderType;
    m_frameType = param.frameType;
    m_frameSize = m_width * m_height * PRIMARY_COLOURS / COMPRESS_RATIO;
    m_cpuCnt = param.cpuCnt;
    m_sliceBased = param.sliceBased;
    m_preset = param.preset;
    m_tune = param.tune;

    if (!SetEncoderName()) {
        return err;
    }

    const AVCodec *codec = avcodec_find_encoder_by_name(this->encoderName);
    if (codec == NULL) {
        ERR("Could not find encoder %s .", this->encoderName);
        return err;
    }
    if (!(this->avCtx = avcodec_alloc_context3(codec))) {
        ERR("Failed to allocate codec context.");
        return err;
    }
    if (!(this->frame = av_frame_alloc())) {
        ERR("Failed to allocate frame.");
        return err;
    }
    this->encPkt = av_packet_alloc();
    //av_init_packet(this->encPkt);
    this->InitParams(param);
    this->frame->width  = this->avCtx->width;
    this->frame->height = this->avCtx->height;
    this->frame->format = this->avCtx->pix_fmt;
    DBG("this->frame->width is: %d, this->frame-height is: %d", this->frame->width, this->frame->height);
    if ((av_frame_get_buffer(this->frame, 4)) < 0) {
        ERR("Failed to get buffer for frame.");
        return err;
    }
    m_status = VMI_ENCODE_ENGINE_INIT;
    mIsReadyForRepeat = false;

    return 0;
}

int RemoteVideoEncoder::StartEncoder()
{
    if (m_status == VMI_ENCODE_ENGINE_INIT || m_status == VMI_ENCODE_ENGINE_STOP) {
        if ((avcodec_open2(this->avCtx, codec, NULL)) < 0) {
            ERR("Cannot open video encoder codec.");
            return -1;
        }
        m_status = VMI_ENCODE_ENGINE_RUNNING;
        return 0;
    }
    return -1;
}

int RemoteVideoEncoder::StopEncoder()
{
    if (m_status == VMI_ENCODE_ENGINE_INIT || m_status == VMI_ENCODE_ENGINE_RUNNING) {
        m_status = VMI_ENCODE_ENGINE_STOP;
        return VMI_SUCCESS;
    }
    ERR("VideoEngine status is wrong...");
    return -1;
}

int RemoteVideoEncoder::EncodeOneFrame(uint8_t *src, uint32_t length, std::pair<uint8_t *, uint32_t> &outData,
                                    VideoParamExt &outVideoParam, bool isRepeat)
{
    if (m_status != VMI_ENCODE_ENGINE_RUNNING || src == nullptr || length != m_frameSize) {
        ERR("Enocde check value failed... %u %u", length, m_frameSize);
        return -1;
    }

    uint64_t startEncodeTs = GetCurrentTimeMs();

    int yLength = this->frame->width * this->frame->height;
    int uvLength = yLength >> 2;
    const unsigned char *srcY = src;
    const unsigned char *srcU = srcY + yLength;
    const unsigned char *srcV = srcU + uvLength;
    memcpy_s(this->frame->data[0], yLength, srcY, (yLength));
    memcpy_s(this->frame->data[1], uvLength, srcU, (uvLength));
    memcpy_s(this->frame->data[2], uvLength, srcV, (uvLength));

    this->frame->pts = this->ptsNo++;
    if (m_keyFrameFlag) {
        this->frame->pict_type = AV_PICTURE_TYPE_I;
    } else {
        this->frame->pict_type = AV_PICTURE_TYPE_P;
    }
    
    mIsReadyForRepeat = true;
    m_repeatWidth = this->frame->width;
    m_repeatHeight = this->frame->height;

    DBG("start to send frame to avcodec %p %u ...this frame pts is:%ld,ptsNo is:%lu", src, length, 
        this->frame->pts, this->ptsNo);
    if (avcodec_send_frame(this->avCtx, this->frame) < 0) {
        ERR("Error sending a frame for encoding.");
        return -1;
    }
    bool isExit = false;
    while (!isExit) {
        int tmp_err = avcodec_receive_packet(this->avCtx, this->encPkt);
        if (tmp_err == AVERROR(EAGAIN)) {
            (void)usleep(1000);  // wait data for 1ms
            WARN("wait data for first frame.");
            return 0;
        }
        isExit = true;
    }

    uint64_t endEncodeTs = GetCurrentTimeMs();
    DBG("Encode one frame done.. time = %ld ms, frame_size=%u", endEncodeTs - startEncodeTs, this->encPkt->size);
    outData.first = this->encPkt->data;
    outData.second = this->encPkt->size;

    this->m_keyFrameFlag = false;    
	this->m_totalCountPerSecond += this->encPkt->size;
    this->m_frameCount++;
    if (isRepeat) {
        this->m_repeatFrameCount++;
    }
    
    this->m_lastEncodeTime = endEncodeTs - startEncodeTs;

    UpdateForOneSecond(endEncodeTs);
    return 0;
}

unsigned int RemoteVideoEncoder::getCurrentFPS(uint64_t &currentTs) 
{
    UpdateForOneSecond(currentTs);

    if (m_frameCount == 0 || currentTs <= m_lastTime) {
        uint32_t fps = m_lastFPS;
        // 当统计周期清零时, 使用清零时记录的fps, 并且考虑当前时间和清零时刻的时间距离。 
        return (unsigned int)(1.0 * fps * ONE_SECOND_IN_MS/(ONE_SECOND_IN_MS + currentTs - m_lastTime));
    }

    if (m_repeatFrameCount == m_frameCount) {
        return (unsigned int)(1.0 * m_frameCount * ONE_SECOND_IN_MS / (currentTs - m_lastTime));
    } else {
        // 当上一秒有抓到图(m_repeatFrameCount!=frames)时，将小数进一，即补帧保守些。 
        return (unsigned int)(1.0 * m_frameCount * ONE_SECOND_IN_MS / (currentTs - m_lastTime) + 0.9999);
    }
}

void RemoteVideoEncoder::UpdateForOneSecond(uint64_t currentTime) {
    if (currentTime - this->m_lastTime >= ONE_SECOND_IN_MS) {
        this->m_lastFPS = (unsigned int)(1.0*m_frameCount*ONE_SECOND_IN_MS/(currentTime - m_lastTime));

        INFO("Encode FPS is:%d(%u) %d, bps is:%d, t=%u",this->m_frameCount, this->m_repeatFrameCount,
        this->m_lastFPS, (this->m_totalCountPerSecond * 8), this->m_lastEncodeTime);
        this->m_lastTime = currentTime;
        this->m_frameCount = 0;
        this->m_totalCountPerSecond = 0;
        this->m_repeatFrameCount = 0;
    }
}

bool RemoteVideoEncoder::GetResetFlag()
{
    return m_resetFlag;
}

void RemoteVideoEncoder::reloadConfig()
{
}